#include <cstdio>
#include <ctime>
#include <exception>
#include <fmt/format.h>
#include <iostream>
#include <source_location>
#include <stdexcept>
#include <string>
using namespace std;

class FileError : public std::exception {
public:
  FileError(const source_location &location) : m_location(location) {}
  const char *what() const noexcept override { return m_message.c_str(); }
  virtual const source_location &where() const noexcept { return m_location; }

protected:
  virtual void setMessage(string message) { m_message = message; }

private:
  string m_message;
  source_location m_location;
};

class FileOpenError : public FileError {
public:
  FileOpenError(string message,
                const source_location &location = source_location::current())
      : FileError(location) {
    setMessage("File open error!" + message);
  }
};
class FileReadError : public FileError {
public:
  FileReadError(string message,
                const source_location &location = source_location::current())
      : FileError{location} {
    setMessage(
        fmt::format("Error reading {},line {}.", message, location.line()));
  }
};

void doSomething() {
  try {
    throw runtime_error("Throw a runtime_error exception!");
  } catch (const runtime_error &e) {
    cout << fmt::format("{} caught a run-time error", __func__) << endl;
    cout << fmt::format("{} throwing FileError exception", __func__) << endl;
    throw_with_nested(
        (FileOpenError{"FileOpenError with nested runtime_error"}));
  }
}

// int main() {
//   try {
//     doSomething();
//   } catch (const FileError &e) {
//     cout << fmt::format("{} caught fileerror: {}", __func__, e.what()) <<
//     endl; const auto *nested{dynamic_cast<const nested_exception *>(&e)}; if
//     (nested) {
//       try {
//         nested->rethrow_nested();
//       } catch (const runtime_error &e) {
//         cout << fmt::format(" Nested exception:{}", e.what()) << endl;
//       }
//     }
//   }
// }

int main() {
  try {
    doSomething();
  } catch (const FileError &e) {
    cout << fmt::format("{} caught fileerror: {}", __func__, e.what()) << endl;

    try {
      rethrow_if_nested(e);
    } catch (const runtime_error &e) {
      cout << fmt::format(" Nested exception:{}", e.what()) << endl;
    }
  }
}